/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


import { ts, ArkFile, AstTreeUtils } from "arkanalyzer/lib";
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import { Defects, IssueReport } from '../../model/Defects';
import { ClassMatcher, FileMatcher, MatcherCallback, MatcherTypes, MethodMatcher } from "../../matcher/Matchers";
import { Utils } from '../../Index';
import { Rule } from "../../model/Rule";
import { RuleListUtil } from "../../utils/common/DefectsList";
import { RuleFix } from '../../model/Fix';
interface Position {
    line: number;
    character: number;
};
interface OptionObject {
    conditionalAssign?: boolean,
    returnAssign?: boolean,
    nestedBinaryExpressions?: boolean,
    ternaryOperandBinaryExpressions?: boolean,
    ignoreJSX?: 'none' | 'all' | 'multi-line' | 'single-line',
    enforceForArrowConditionals?: boolean,
    enforceForSequenceExpressions?: boolean,
    enforceForNewInMemberExpressions?: boolean,
    enforceForFunctionPrototypeMethods?: boolean,
    allowParensAfterCommentPattern?: 'any-string-pattern' | '@type'
};
type OptionKey = 'all' | 'functions';
type OptionsTuple = [OptionKey?, OptionObject?];
const defaultOptions: OptionsTuple = [
    'all',
    {
        conditionalAssign: true,
        returnAssign: true,
        nestedBinaryExpressions: true,
        ternaryOperandBinaryExpressions: true,
        ignoreJSX: 'none',
        enforceForArrowConditionals: true,
        enforceForSequenceExpressions: true,
        enforceForNewInMemberExpressions: true,
        enforceForFunctionPrototypeMethods: true,
        allowParensAfterCommentPattern: 'any-string-pattern'
    }
];
const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'NoExtraParensCheck');
const gmetaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: "docs/no-extra-parens-check.md", // TODO: support url
    description: "Unnecessary parentheses around expression."
};
export class NoExtraParensCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gmetaData;
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];
    private fileMatcher: FileMatcher = {
        matcherType: MatcherTypes.FILE,
    };
    private classMatcher: ClassMatcher = {
        file: [this.fileMatcher],
        matcherType: MatcherTypes.CLASS
    };
    private methodMatcher: MethodMatcher = {
        matcherType: MatcherTypes.METHOD,
        class: [this.classMatcher]
    };
    public registerMatchers(): MatcherCallback[] {
        const matchBuildCb: MatcherCallback = {
            matcher: this.fileMatcher,
            callback: this.check
        }
        return [matchBuildCb];
    }
    public issueMap: Map<string, IssueReport> = new Map();
    public check = (file: ArkFile) => {
        let options: OptionsTuple;
        if (this.rule && this.rule.option.length > 0) {
            options = this.rule.option as OptionsTuple;
        } else {
            options = defaultOptions;
        }
        this.issueMap.clear();
        const code = file.getCode() ?? '';
        this.checkUnnecessaryParentheses(file, code, options);
        this.reportSortedIssues();
    }
    private checkUnnecessaryParentheses(file: ArkFile, code: string, options: OptionsTuple): Position[] {
        const sourceFile = AstTreeUtils.getASTNode('temp.ts', code);
        const unnecessaryParentheses: Position[] = [];
        if (options[0] === 'all') {
            this.handleAllOptions(file, code, options, sourceFile, unnecessaryParentheses);
        }
        else if (options[0] === 'functions') {
            this.handleFunctionsOptions(file, sourceFile, unnecessaryParentheses);
        } else {
            this.handleOtherOptions(file, options, sourceFile, unnecessaryParentheses);
        }
        return unnecessaryParentheses;
    }
    // 处理 'all' 选项的逻辑
    private handleAllOptions(file: ArkFile, code: string, options: OptionsTuple,
        sourceFile: ts.SourceFile, unnecessaryParentheses: Position[]): void {
        const optionConfig = options[1] || {};
        const checker = new AllOptionsChecker(this, file, optionConfig);
        checker.check(sourceFile, unnecessaryParentheses);
    }
    // 处理 'functions' 选项的逻辑
    private handleFunctionsOptions(file: ArkFile, sourceFile: ts.SourceFile,
        unnecessaryParentheses: Position[]): void {
        const checker = new FunctionOptionsChecker(this, file);
        checker.check(sourceFile, unnecessaryParentheses);
    }
    // 处理其他选项的逻辑
    private handleOtherOptions(file: ArkFile, options: OptionsTuple,
        sourceFile: ts.SourceFile, unnecessaryParentheses: Position[]): void {
        const option = options[0] as OptionObject;
        const checker = new AllOptionsChecker(this, file, option);
        checker.check(sourceFile, unnecessaryParentheses);
    }
    public ruleFix(pos: number, end: number, fixText: string): RuleFix {
        return { range: [pos, end], text: fixText };
    }
    private reportSortedIssues(): void {
        if (this.issueMap.size === 0) {
            return;
        }

        const sortedIssues = Array.from(this.issueMap.entries())
            .sort(([keyA], [keyB]) => Utils.sortByLineAndColumn(keyA, keyB));

        this.issues = [];

        sortedIssues.forEach(([_, issue]) => {
            RuleListUtil.push(issue.defect);
            this.issues.push(issue);
        });
    }

    public addIssueReport(arkFile: ArkFile, lineAndColumn: Position): Defects {
        const severity = this.rule.alert ?? this.metaData.severity;
        const warnInfo = this.getLineAndColumn(arkFile, lineAndColumn);
        this.metaData.description = this.metaData.description.padEnd(130);
        const filePath = arkFile.getFilePath();
        let defects = new Defects(warnInfo.line, warnInfo.startCol, warnInfo.endCol, this.metaData.description,
            severity, this.rule.ruleId, filePath, this.metaData.ruleDocPath, true, false, true);
        this.defects.push(defects);
        return defects;
    }
    public getLineAndColumn(arkfile: ArkFile, lineColumn: Position): { line: number; startCol: number; endCol: number; filePath: string; } {
        if (arkfile) {
            const originPath = arkfile.getFilePath();
            return { line: lineColumn.line, startCol: lineColumn.character, endCol: lineColumn.character, filePath: originPath };
        } else {
            logger.debug('arkFile is null');
        }
        return { line: -1, startCol: -1, endCol: -1, filePath: '' };
    }
}
class AllOptionsChecker {
    constructor(
        private parent: NoExtraParensCheck,
        private file: ArkFile,
        private options: OptionObject
    ) { }
    check(sourceFile: ts.SourceFile, unnecessaryParentheses: Position[]): void {
        const traverse = (node: ts.Node): void => {
            if (!node) {
                return;
            }
            if (ts.isParenthesizedExpression(node)) {
                const innerExpression = node?.expression;
                const parent = innerExpression?.parent;
                // 判断括号内的表达式是否为简单表达式，并且括号不是用来改变运算优先级的
                this.check1(node, parent, sourceFile, unnecessaryParentheses);
            }
            ts.forEachChild(node, childNode => traverse(childNode));
        }
        traverse(sourceFile);
    }
    private check1(node: ts.ParenthesizedExpression, parent: ts.Node, sourceFile: ts.SourceFile, unnecessaryParentheses: Position[]): void {
        if (this.isSimpleExpression(node) &&
            !(ts.isBinaryExpression(parent) && [
                ts.SyntaxKind.PlusToken, ts.SyntaxKind.MinusToken, ts.SyntaxKind.AsteriskToken, ts.SyntaxKind.SlashToken,
                ts.SyntaxKind.PercentToken, ts.SyntaxKind.LessThanToken, ts.SyntaxKind.LessThanEqualsToken,
                ts.SyntaxKind.GreaterThanToken, ts.SyntaxKind.GreaterThanEqualsToken, ts.SyntaxKind.EqualsEqualsToken,
                ts.SyntaxKind.EqualsEqualsEqualsToken, ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken,
                ts.SyntaxKind.CaretToken
            ].includes(parent?.operatorToken?.kind))) {
            this.check11(node, sourceFile, unnecessaryParentheses);
        }
    }
    private check11(node: ts.ParenthesizedExpression, sourceFile: ts.SourceFile, unnecessaryParentheses: Position[]): void {
        const startPosition = node.getStart();
        const endPosition = node.getEnd();
        const positionInfo = sourceFile.getLineAndCharacterOfPosition(startPosition);
        const fixText = node.expression.getText();

        const currentPosition = {
            line: positionInfo.line + 1,
            character: positionInfo.character + 1
        };

        const fixKey = `${currentPosition.line}%${currentPosition.character}%${this.parent.rule.ruleId}`;
        if (!this.parent.issueMap.has(fixKey)) {
            const defect = this.parent.addIssueReport(this.file, currentPosition);
            const fix = this.parent.ruleFix(startPosition, endPosition, fixText);
            this.parent.issueMap.set(fixKey, { defect, fix });
        }

        // 仅记录当前 node 的位置到数组
        unnecessaryParentheses.push(currentPosition);
    }
    private extractOutermostObject(node: ts.Node): string | null {
        if (!node) {
            return null;
        }
        while (ts.isPropertyAccessExpression(node)) {
            // 获取属性访问表达式的对象部分
            node = node.expression;
        }

        // 如果最外层对象是一个标识符，则返回其文本
        if (ts.isIdentifier(node)) {
            return node.getText();
        }

        // 如果不是标识符，或者无法确定最外层对象，则返回 null
        return null;
    }
    private isAssignmentExpression(node: ts.Node): boolean {
        if (!node) {
            return false;
        }
        // 检查节点是否为二元表达式，并且运算符是否为等号
        return ts.isBinaryExpression(node) && node.operatorToken?.kind === ts.SyntaxKind?.EqualsToken;
    }
    private isWithinFunctionOrArrowFunction(node: ts.Node): boolean {
        if (!node) {
            return false;
        }
        // 递归地向上遍历 AST，直到找到顶层节点或函数/箭头函数节点
        while (node && !(ts.isFunctionDeclaration(node) || ts.isArrowFunction(node))) {
            node = node.parent;
        }
        return ts.isFunctionDeclaration(node) || ts.isArrowFunction(node);
    }
    private isWithinReturnOrArrowFunction(node: ts.Node): boolean {
        if (!node) {
            return false;
        }
        // 递归地向上遍历 AST，直到找到顶层节点或返回语句/箭头函数节点
        while (node && !(ts.isReturnStatement(node) || ts.isArrowFunction(node))) {
            node = node.parent;
        }
        if (node) {
            return ts?.isReturnStatement(node) || ts?.isArrowFunction(node);
        }
        return false;
    }
    private findFunctionExpression(node: ts.Node): ts.FunctionExpression | null {
        if (!node) {
            return null;
        }
        if (ts.isFunctionExpression(node)) {
            return node;
        }
        for (const child of node.getChildren()) {
            const result = this.findFunctionExpression(child);
            if (result !== null) {
                return result;
            }
        }
        return null;
    }
    private findRegExpExpression(node: ts.Node): boolean {
        if (!node) {
            return false;
        }
        const text = node.getText();
        const typeAssertionCommentRegex = /\/\*\*\s*@type\s*{\s*[^}]*\s*}\s*\*\//;
        if (typeAssertionCommentRegex.test(text)) {
            // 如果括号内包含类型断言注释，则认为括号是不必要的
            return true;
        }
        for (const child of node.getChildren()) {
            if (this.findRegExpExpression(child)) {
                return true;
            }
        }
        return false;
    }
    private findGenericExpression(node: ts.Node): boolean {
        if (!node) {
            return false;
        }
        let found = { value: false };
        node.forEachChild(childNode => {
            if (ts.isParenthesizedExpression(childNode) && ts.isLiteralExpression(childNode.expression)) {
                this.findGenericExpression1(node, childNode, found);
            }
        });
        return found.value;
    }
    private findGenericExpression1(node: ts.Node, childNode: ts.Node, found: { value: boolean }): void {
        if (ts.isParenthesizedExpression(childNode) && ts.isLiteralExpression(childNode.expression)) {
            node.forEachChild(childNode => {
                const paText = node.getText();
                const text = childNode.getText();
                const target = `<${text}>`;
                if (paText.includes(target)) {
                    found.value = true;
                }
            });
        }
    }
    private checkAsExpressionAlsoConditionalExpression(expression: ts.Expression, parent: ts.Node, options: OptionObject): boolean | void {
        if (ts.isAsExpression(expression) && ts.isConditionalExpression(parent)) {
            if (options.nestedBinaryExpressions !== undefined) {
                if (options.nestedBinaryExpressions === false) {
                    return false;
                } else {
                    return true;
                }
            } else {
                return true;
            }
        }
        return undefined;
    }
    private checkAsExpressionAlsoBinaryExpression(expression: ts.Expression, parent: ts.Node): boolean | void {
        if (ts.isAsExpression(expression)) {
            // 如果父节点是二元运算符或条件表达式，并且括号不是用来改变运算优先级的，则认为括号是不必要的
            if (ts.isBinaryExpression(parent)) {
                this.checkAsExpressionAlsoBinaryExpression1(parent);
            }
        }
        return undefined;
    }
    private checkAsExpressionAlsoBinaryExpression1(parent: ts.BinaryExpression): boolean {
        if (parent.operatorToken) {
            if (![
                ts.SyntaxKind.PlusToken, ts.SyntaxKind.MinusToken, ts.SyntaxKind.AsteriskToken, ts.SyntaxKind.SlashToken,
                ts.SyntaxKind.PercentToken, ts.SyntaxKind.LessThanToken, ts.SyntaxKind.LessThanEqualsToken,
                ts.SyntaxKind.GreaterThanToken, ts.SyntaxKind.GreaterThanEqualsToken, ts.SyntaxKind.EqualsEqualsToken,
                ts.SyntaxKind.EqualsEqualsEqualsToken, ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken,
                ts.SyntaxKind.CaretToken
            ].includes(parent.operatorToken.kind) || ts.isConditionalExpression(parent)) {
                return true;
            }
            return false;
        } else {
            return false;
        }
    }
    private checkTypeAssertionExpressionAlsoConditionalExpression(expression: ts.Expression, parent: ts.Node, options: OptionObject): boolean | void {
        // 检查类型断言表达式
        if (ts.isTypeAssertionExpression(expression) && ts.isConditionalExpression(parent)) {
            if (options.nestedBinaryExpressions !== undefined) {
                if (options.nestedBinaryExpressions === false) {
                    return false;
                } else {
                    return true;
                }
            } else {
                return true;
            }
        }
        return undefined;
    }
    private checkFunctionExpressionAlsoConditionalExpression(node: ts.ParenthesizedExpression, parent: ts.Node, options: OptionObject): boolean | void {
        if (this.findFunctionExpression(node)) {
            if (ts.isConditionalExpression(parent)) {
                return false;
            }
            // 添加 return 语句
            return this.checkFunctionExpressionAlsoConditionalExpression1(options);
        }
        // 添加默认返回值
        return undefined;
    }
    private checkFunctionExpressionAlsoConditionalExpression1(options: OptionObject): boolean {
        if (options.enforceForFunctionPrototypeMethods !== undefined) {
            return options.enforceForFunctionPrototypeMethods !== false;
        }
        // 明确返回默认值
        return true;
    }
    private checkBinaryExpressionWhileIf(expression: ts.Expression, parent: ts.Node, options: OptionObject): boolean | void {
        if (this.isAssignmentInControlFlow(expression, parent)) {
            return this.handleConditionalAssignOption(options);
        }
        return undefined;
    }

    private isAssignmentInControlFlow(expression: ts.Expression, parent: ts.Node): boolean {
        return ts.isBinaryExpression(expression) &&
            expression?.operatorToken?.kind === ts.SyntaxKind?.EqualsToken &&
            this.isControlFlowParent(parent);
    }

    private isControlFlowParent(parent: ts.Node): boolean {
        return ts.isWhileStatement(parent) ||
            ts.isIfStatement(parent) ||
            ts.isForStatement(parent) ||
            ts.isDoStatement(parent) ||
            ts.isSwitchStatement(parent);
    }

    private handleConditionalAssignOption(options: OptionObject): boolean {
        if (options.conditionalAssign !== undefined) {
            return options.conditionalAssign === true;
        }
        return true;
    }
    private checkAssignment(node: ts.ParenthesizedExpression, options: OptionObject): void {
        if (ts.isParenthesizedExpression(node)) {
            const expression = node?.expression;
            if (this.isAssignmentExpression(expression)) {
                const parent = node?.parent;
                if (parent && this.isWithinReturnOrArrowFunction(parent)) {
                    this.checkAssignment1(parent, options);
                }
            }
        }
    }
    private checkAssignment1(parent: ts.Node, options: OptionObject): boolean | void {
        if (this.isWithinFunctionOrArrowFunction(parent)) {
            if (options.returnAssign !== undefined) {
                this.checkAssignment11(options);
            }
            else {
                return true;
            }
        }
        return undefined;
    }
    private checkAssignment11(options: OptionObject): boolean {
        if (options.returnAssign === false) {
            return false;
        } else {
            return true;
        }
    }
    private checkBinaryExpression(parent: ts.Node, options: OptionObject): boolean | void {
        if (ts.isBinaryExpression(parent)) {
            const operator = parent?.operatorToken?.kind;
            if ([ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken].includes(operator)) {
                this.checkBinaryExpression1(options);
            }
            if ([ts.SyntaxKind.PlusToken, ts.SyntaxKind.MinusToken, ts.SyntaxKind.AsteriskToken,
            ts.SyntaxKind.SlashToken, ts.SyntaxKind.PercentToken].includes(operator)) {
                this.checkBinaryExpression1(options);
            }
        }
    }
    private checkBinaryExpression1(options: OptionObject): boolean {
        if (options.nestedBinaryExpressions !== undefined) {
            if (options.nestedBinaryExpressions === false) {
                return false;
            } else {
                return true;
            }
        }
        else {
            return true;
        }
    }
    private checkConditionalExpression(parent: ts.Node, options: OptionObject): void {
        if (ts.isConditionalExpression(parent)) {
            // 获取三元运算符的条件、真值和假值部分
            const condition = parent?.condition;
            const whenTrue = parent?.whenTrue;
            const whenFalse = parent?.whenFalse;
            const isConditionParenthesized = ts.isParenthesizedExpression(condition as ts.Node);
            const isWhenTrueParenthesized = ts.isParenthesizedExpression(whenTrue as ts.Node);
            const isWhenFalseParenthesized = ts.isParenthesizedExpression(whenFalse as ts.Node);
            // 如果三元运算符的任何操作数被单独括号包围，则返回false（表示这些括号不是多余的）
            if (isConditionParenthesized || isWhenTrueParenthesized || isWhenFalseParenthesized) {
                this.checkConditionalExpression1(options);
            }
        }
    }
    private checkConditionalExpression1(options: OptionObject): boolean {
        if (options.ternaryOperandBinaryExpressions !== undefined) {
            if (options.ternaryOperandBinaryExpressions === false) {
                return false;
            } else {
                return true;
            }
        } else {
            return true;
        }
    }
    private checkArrowConditional(parent: ts.Node, options: OptionObject): void {
        if (ts.isArrowFunction(parent)) {
            const body = parent?.body;
            if (ts.isParenthesizedExpression(body)) {
                const expression = body?.expression;
                if (ts.isConditionalExpression(expression)) {
                    this.checkArrowConditional1(expression, options);
                }
            }
        }
    }
    private checkArrowConditional1(expression: ts.Expression, options: OptionObject): boolean | void {
        if (ts.isConditionalExpression(expression)) {
            if (options.enforceForArrowConditionals !== undefined) {
                this.checkArrowConditional11(options);
            } else {
                return true;
            }
        }
        return undefined;
    }
    private checkArrowConditional11(options: OptionObject): boolean {
        if (options.enforceForArrowConditionals === false) {
            return false;
        } else {
            return true;
        }
    }
    private checkBinary(expression: ts.Expression, options: OptionObject): void {
        if (ts.isBinaryExpression(expression) && expression?.operatorToken?.kind === ts.SyntaxKind?.CommaToken) {
            if (ts.isParenthesizedExpression(expression.parent as ts.Node)) {
                this.checkBinary1(options);
            }
        }
    }
    private checkBinary1(options: OptionObject): boolean {
        if (options.enforceForSequenceExpressions !== undefined) {
            if (options.enforceForSequenceExpressions === false) {
                return false;
            } else {
                return true;
            }
        } else {
            return true;
        }
    }
    private checkNewExpression(node: ts.ParenthesizedExpression, options: OptionObject): boolean | undefined {
        if (ts.isParenthesizedExpression(node) && ts.isNewExpression(node.expression)) {
            if (options.enforceForNewInMemberExpressions !== undefined) {
                if (options.enforceForNewInMemberExpressions === false) {
                    return false;
                } else {
                    return true;
                }
            } else {
                return true;
            }
        }
        return undefined;
    }
    private checkRegExpExpression(parent: ts.Node, options: OptionObject): boolean | undefined {
        if (this.findRegExpExpression(parent)) {
            if (options.allowParensAfterCommentPattern !== undefined) {
                if (options.allowParensAfterCommentPattern === '@type') {
                    return false;
                } else {
                    return true;
                }
            } else {
                return true;
            }
        }
        return undefined;
    }
    private checkCallExpression(expression: ts.Expression, parent: ts.Node): boolean | undefined {
        // 检查括号内是否为方法调用，并且该方法调用本身不需要括号
        if (ts.isCallExpression(expression) && !ts.isParenthesizedExpression(expression)) {
            // 如果父节点不是需要改变运算优先级的节点，则认为是不必要的括号
            if (!(ts.isBinaryExpression(parent) && [
                ts.SyntaxKind.PlusToken, ts.SyntaxKind.MinusToken, ts.SyntaxKind.AsteriskToken, ts.SyntaxKind.SlashToken,
                ts.SyntaxKind.PercentToken, ts.SyntaxKind.LessThanToken, ts.SyntaxKind.LessThanEqualsToken,
                ts.SyntaxKind.GreaterThanToken, ts.SyntaxKind.GreaterThanEqualsToken, ts.SyntaxKind.EqualsEqualsToken,
                ts.SyntaxKind.EqualsEqualsEqualsToken, ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken,
                ts.SyntaxKind.CaretToken
            ].includes(parent?.operatorToken?.kind)) &&
                !(ts.isConditionalExpression(parent))
            ) {
                return true;
            }
        }
        return undefined;
    }
    private isSimpleExpression(node: ts.Node): boolean {
        const nText = node.getText();
        if (!node || nText.startsWith('((')) {
            return false;
        }
        if (this.options && ts.isParenthesizedExpression(node)) {
            return this.checkParenthesizedExpression(node);
        }
        return this.checkBaseCases(node);
    }

    private checkParenthesizedExpression(node: ts.ParenthesizedExpression): boolean {
        const parent = node.parent;
        const expression = node.expression;
        const text = expression?.getText();

        if (this.findGenericExpression(parent)) { return true };

        return this.checkExpressionCases(node, parent, expression, text);
    }

    private checkExpressionCases(node: ts.ParenthesizedExpression, parent: ts.Node,
        expression: ts.Expression, text: string | undefined): boolean {

        // 检查箭头函数作为条件表达式的一部分
        if (ts.isArrowFunction(expression) && ts.isConditionalExpression(parent)) {
            return false;
        }

        // 检查各种表达式类型
        const checks = [
            (): boolean | void => this.checkAsExpressionCases(expression, parent),
            (): boolean | void => this.checkTypeAssertionCases(expression, parent),
            (): boolean | void => this.checkFunctionExpressionCase(node, parent),
            (): boolean | void => this.checkCommaExpressionCase(text, parent),
            (): boolean | void => this.checkPropertyAccess(expression),
            (): boolean | void => this.checkControlFlowExpressions(expression, parent),
            (): boolean | void => this.checkSpecialExpressionTypes(node, parent, expression)
        ];

        for (const check of checks) {
            const result = check();
            if (result !== undefined) { return result };
        }

        if (ts.isPropertyAccessExpression(parent) || ts.isCallExpression(parent)) {
            return false;
        }
        return true;
    }

    private checkAsExpressionCases(expression: ts.Expression, parent: ts.Node): boolean | void {
        const checkAsConditional = this.checkAsExpressionAlsoConditionalExpression(expression, parent, this.options);
        if (checkAsConditional !== undefined) { return checkAsConditional };

        const checkAsBinary = this.checkAsExpressionAlsoBinaryExpression(expression, parent);
        if (checkAsBinary !== undefined) { return checkAsBinary };

        if (ts.isAsExpression(expression) && !ts.isParenthesizedExpression(expression)) {
            return true;
        }
        return undefined;
    }

    private checkTypeAssertionCases(expression: ts.Expression, parent: ts.Node): boolean | void {
        const checkTypeAssert = this.checkTypeAssertionExpressionAlsoConditionalExpression(
            expression, parent, this.options);
        if (checkTypeAssert !== undefined) { return checkTypeAssert };
        return undefined;
    }

    private checkFunctionExpressionCase(node: ts.ParenthesizedExpression, parent: ts.Node): boolean | void {
        const checkFunction = this.checkFunctionExpressionAlsoConditionalExpression(
            node, parent, this.options
        );
        if (checkFunction !== undefined) { return checkFunction }
        return undefined;
    }

    private checkCommaExpressionCase(text: string | undefined, parent: ts.Node): boolean | void {
        if (text?.includes(',')) {
            return !ts.isForOfStatement(parent);
        }
        return undefined;
    }

    private checkPropertyAccess(expression: ts.Expression): boolean | void {
        let hasInvalidAccess = false;
        let hasAnyAccess = false;

        ts.forEachChild(expression, child => {
            if (ts.isPropertyAccessExpression(child)) {
                hasAnyAccess = true;
                const isValid = !!this.extractOutermostObject(child);
                if (!isValid) {
                    hasInvalidAccess = true;
                    return; // 发现无效立即终止遍历
                }
            }
        });
        if (hasInvalidAccess) { return false }
        if (hasAnyAccess) { return true }
        return undefined;
    }

    private checkControlFlowExpressions(expression: ts.Expression, parent: ts.Node): boolean | void {
        return this.checkBinaryExpressionWhileIf(expression, parent, this.options);
    }

    private checkSpecialExpressionTypes(node: ts.ParenthesizedExpression, parent: ts.Node,
        expression: ts.Expression): boolean | undefined {

        let result: boolean | undefined;
        const checks = [
            (): void => this.checkAssignment(node, this.options),
            (): boolean | void => this.checkBinaryExpression(parent, this.options),
            (): boolean | void => this.checkConditionalExpression(parent, this.options),
            (): boolean | void => this.checkArrowConditional(parent, this.options),
            (): boolean | void => this.checkBinary(expression, this.options),
            (): boolean | undefined => this.checkNewExpression(node, this.options),
            (): boolean | undefined => this.checkRegExpExpression(parent, this.options),
            (): boolean | undefined => this.checkCallExpression(expression, parent)
        ];

        for (const check of checks) {
            const checkResult = check();
            if (checkResult !== undefined) {
                result = checkResult;
                break;
            }
        }
        return result;
    }

    private checkBaseCases(node: ts.Node): boolean {
        return ts.isIdentifier(node) ||
            ts.isLiteralExpression(node) ||
            (ts.isBinaryExpression(node) && this.isSimpleExpression(node.left) && this.isSimpleExpression(node.right)) ||
            (ts.isPrefixUnaryExpression(node) && this.isSimpleExpression(node.operand)) ||
            (ts.isPostfixUnaryExpression(node) && this.isSimpleExpression(node.operand)) ||
            (ts.isParenthesizedExpression(node) && this.isSimpleExpression(node.expression));
    }
}
class FunctionOptionsChecker {
    constructor(
        private parent: NoExtraParensCheck,
        private file: ArkFile,
    ) { }

    check(sourceFile: ts.SourceFile, unnecessaryParentheses: Position[]) {
        const traverse = this.createTraverseFunction(unnecessaryParentheses);
        traverse(sourceFile, sourceFile, unnecessaryParentheses);
    }

    private createTraverseFunction(unnecessaryParentheses: Position[]): (node: ts.Node, sourceFile: ts.SourceFile,
        _unnecessaryParentheses: Position[]) => void {
        return (node: ts.Node, sourceFile: ts.SourceFile, _unnecessaryParentheses: Position[]) => {
            if (!node) { return };

            this.handleParenthesizedExpression(node, sourceFile, unnecessaryParentheses);
            this.traverseChildNodes(node, sourceFile, unnecessaryParentheses);
        };
    }

    private handleParenthesizedExpression(node: ts.Node, sourceFile: ts.SourceFile, unnecessaryParentheses: Position[]): void {
        if (ts.isParenthesizedExpression(node) && this.isUnnecessaryFunctionParentheses(node) && this.conditionalCheck(node)) {
            const positionInfo = this.getPositionInfo(node, sourceFile);
            this.reportDefects(node, positionInfo, unnecessaryParentheses);
        }
    }

    private conditionalCheck(node: ts.Node,): boolean {
        const parent = node.parent;
        if (ts.isConditionalExpression(parent) && ts.isParenthesizedExpression(parent.condition)) {
            return false;
        }
        return true;
    }

    private getPositionInfo(node: ts.ParenthesizedExpression, sourceFile: ts.SourceFile): { start: number; end: number; position: ts.LineAndCharacter; } {
        return {
            start: node.getStart(),
            end: node.getEnd(),
            position: sourceFile.getLineAndCharacterOfPosition(node.getStart())
        };
    }
    private findFunctionExpression(node: ts.Node): ts.FunctionExpression | null {
        if (!node) {
            return null;
        }
        if (ts.isFunctionExpression(node)) {
            return node;
        }
        for (const child of node?.getChildren()) {
            const result = this.findFunctionExpression(child);
            if (result !== null) {
                return result;
            }
        }
        return null;
    }
    private reportDefects(node: ts.ParenthesizedExpression, positionInfo: any, unnecessaryParentheses: Position[]): void {
        unnecessaryParentheses.push({
            line: positionInfo.position.line + 1,
            character: positionInfo.position.character + 1
        });

        unnecessaryParentheses.forEach(unnecessaryParenthese => {
            this.createDefectAndFix(node, unnecessaryParenthese);
        });
    }

    private createDefectAndFix(node: ts.ParenthesizedExpression, unnecessaryParenthese: Position): void {
        const fixKey = `${unnecessaryParenthese.line}%${unnecessaryParenthese.character}%${this.parent.rule.ruleId}`;
        if (!this.parent.issueMap.get(fixKey)) {
            const defect = this.parent.addIssueReport(this.file, unnecessaryParenthese);
            const fix = this.parent.ruleFix(node.getStart(), node.getEnd(), node.expression.getText());
            this.parent.issueMap.set(defect.fixKey, { defect, fix });
        }
    }

    private traverseChildNodes(node: ts.Node, sourceFile: ts.SourceFile, unnecessaryParentheses: Position[]): void {
        ts.forEachChild(node, childNode =>
            this.createTraverseFunction(unnecessaryParentheses)(childNode, sourceFile, unnecessaryParentheses)
        );
    }
    private isUnnecessaryFunctionParentheses(node: ts.Node): boolean {
        if (!node || !ts.isParenthesizedExpression(node)) { return false };

        const expression = node.expression;
        const functionExpr = this.findFunctionExpression(expression);
        if (!functionExpr) { return false };

        return this.performAllParenthesesChecks(node, expression, functionExpr);
    }

    private performAllParenthesesChecks(node: ts.ParenthesizedExpression,
        expression: ts.Expression,
        functionExpr: ts.FunctionExpression): boolean {
        const parent = expression.parent;
        return [
            (): boolean | null => this.checkNestedParentheses(node, expression),
            (): boolean => this.checkBinaryExpressionParent(parent, functionExpr),
            (): boolean => this.checkConditionalExpressionParent(parent, functionExpr),
            (): boolean => this.checkNestedConditionalExpression(expression, parent, functionExpr),
            (): boolean | undefined => this.checkParentConditionalStructure(parent)
        ].some(check => check() === false);
    }

    private checkNestedParentheses(node: ts.ParenthesizedExpression, expression: ts.Expression): boolean | null {
        return this.findFunctionExpression(node) &&
            ts.isParenthesizedExpression(expression);
    }

    private checkBinaryExpressionParent(parent: ts.Node, functionExpr: ts.FunctionExpression): boolean {
        return ts.isBinaryExpression(parent) &&
            (parent.left === functionExpr || parent.right === functionExpr);
    }

    private checkConditionalExpressionParent(parent: ts.Node, functionExpr: ts.FunctionExpression): boolean {
        return ts.isConditionalExpression(parent) &&
            [parent.condition, parent.whenTrue, parent.whenFalse].includes(functionExpr);
    }

    private checkNestedConditionalExpression(expression: ts.Expression,
        parent: ts.Node,
        functionExpr: ts.FunctionExpression): boolean {
        return ts.isConditionalExpression(expression) &&
            ts.isFunctionExpression(expression.condition) &&
            expression.condition === functionExpr &&
            ts.isParenthesizedExpression(parent);
    }

    private checkParentConditionalStructure(parent: ts.Node): boolean | undefined {
        const conditionalParent = parent.parent;
        if (!ts.isConditionalExpression(conditionalParent)) { return undefined };

        const checkOperand = (operand: ts.Node): boolean =>
            ts.isParenthesizedExpression(operand as ts.Node);

        return [
            conditionalParent.condition,
            conditionalParent.whenTrue,
            conditionalParent.whenFalse
        ].some(checkOperand);
    }
}